﻿#include "../../src/detail/coro_runner_impl.hh"
#include "../../src/util/string_util.hh"
#include "../../src/util/time_util.hh"
#include "../framework/unittest.hh"
#include "../root/coroutine/coroutine.h"
#include <fstream>

FIXTURE_BEGIN(test_coro_runner)

using namespace kratos;

CASE(TestStart1) {
  service::CoroRunnerImpl cm(nullptr);
  bool done = false;
  cm.start_co([&]() { done = true; });
  ASSERT_TRUE(done);
}

CASE(TestStart2) {
  service::CoroRunnerImpl cm(nullptr);
  cm.start_co(nullptr);
}

CASE(TestStart3) {
  service::CoroRunnerImpl cm(nullptr);
  cm.start_co([&]() { throw std::runtime_error("error"); });
  auto start = util::get_os_time_millionsecond();
  while (true) {
    coro_sched();
    if (util::get_os_time_millionsecond() - start > 100) {
      break;
    }
  }
}

CASE(TestStopAll1) {
  service::CoroRunnerImpl cm(nullptr);
  // 不能在主协程内睡眠
  cm.sleep_co(1000);
  bool done = false;
  bool run = false;
  cm.start_co([&]() {
    run = true;
    cm.sleep_co(1000);
    done = true;
  });
  cm.stop_all_co();
  ASSERT_TRUE(!done && run);
}

// 可以正确释放栈上对象
CASE(TestStopAll2) {
  static bool done = false;
  struct Fool {
    ~Fool() { done = true; }
  };
  service::CoroRunnerImpl cm(nullptr);
  cm.start_co([&]() {
    Fool fool;
    cm.sleep_co(1000);
  });
  cm.stop_all_co();
  ASSERT_TRUE(done);
}

CASE(TestYield1) {
  service::CoroRunnerImpl cm(nullptr);
  bool done = false;
  auto id = cm.start_co([&]() {
    cm.yield_co();
    done = true;
  });
  cm.resume_co(id);
  ASSERT_TRUE(done);
}

CASE(TestException1) {
  service::CoroRunnerImpl cm(nullptr);
  cm.start_co([&]() { throw std::runtime_error(""); });
}

CASE(TestException2) {
  service::CoroRunnerImpl cm(nullptr);
  bool done = false;
  auto id = cm.start_co([&]() {
    try {
      cm.yield_co();
    } catch (CoroCancelException &) {
    }
    done = true;
  });
  cm.cancel_co(id);
  ASSERT_TRUE(done);
}

CASE(TestPushCo1) {
  service::CoroRunnerImpl cm(nullptr);
  bool done = false;
  auto id = cm.start_co([&]() {
    auto data = cm.wait_co();
    done = data.has_value();
  });
  std::any str("123");
  cm.push_co(id, str);
  ASSERT_TRUE(done);
}

CASE(TestTryCo1) {
  service::CoroRunnerImpl cm(nullptr);
  bool done = false;
  auto id = cm.start_co([&]() {
    auto data = cm.try_co();
    done = !data.has_value();
  });
  ASSERT_TRUE(done);
}

CASE(TestWaitCo1) {
  service::CoroRunnerImpl cm(nullptr);
  bool done = false;
  auto id = cm.start_co([&]() {
    auto data = cm.wait_co(1000);
    done = data.has_value();
  });
  std::any str("123");
  cm.push_co(id, str);
  ASSERT_TRUE(done);
}

CASE(TestAwaitCancelCo1) {
  service::CoroRunnerImpl cm(nullptr);
  bool cancel1 = false;
  auto id = cm.start_co([&]() {
    cm.await_cancel_co(200, [&]() {
      cm.start_co([&]() {
        try {
          cm.yield_co();
          return;
        } catch (...) {
          cancel1 = true;
        }
      });
    });
  });

  auto start = util::get_os_time_millionsecond();
  while (true) {
    coro_sched();
    if (util::get_os_time_millionsecond() - start > 500) {
      break;
    }
  }
  ASSERT_TRUE(cancel1);
}

CASE(TestAwaitCancelCo2) {
  service::CoroRunnerImpl cm(nullptr);
  bool cancel1 = false;
  cm.start_co([&] {
    cm.await_cancel_co(200, [&]() {
      cm.start_co([&]() {
        try {
          cm.yield_co();
          return;
        } catch (...) {
          cancel1 = true;
        }
      });
    });
  });

  auto start = util::get_os_time_millionsecond();
  while (true) {
    coro_sched();
    if (util::get_os_time_millionsecond() - start > 500) {
      break;
    }
  }
  ASSERT_TRUE(cancel1);
}

CASE(TestAwaitCancelCo3) {
  service::CoroRunnerImpl cm(nullptr);
  bool cancel1 = false;
  cm.start_co([&] {
    cm.await_cancel_co(200, [&]() {
      try {
        cm.yield_co();
        return;
      } catch (...) {
        cancel1 = true;
      }
    });
  });

  auto start = util::get_os_time_millionsecond();
  while (true) {
    coro_sched();
    if (util::get_os_time_millionsecond() - start > 500) {
      break;
    }
  }
  ASSERT_TRUE(cancel1);
}

CASE(TestAwaitCancelCo4) {
  service::CoroRunnerImpl cm(nullptr);
  bool cancel1 = false;
  cm.await_cancel_co(200, [&]() {
    try {
      return;
    } catch (...) {
      cancel1 = true;
    }
  });
  ASSERT_TRUE(!cancel1);
}

CASE(TestAwaitCancelCo5) {
  service::CoroRunnerImpl cm(nullptr);
  bool done = true;
  cm.start_co([&] { done = cm.await_cancel_co(200, nullptr); });
  auto start = util::get_os_time_millionsecond();
  while (true) {
    coro_sched();
    if (util::get_os_time_millionsecond() - start > 500) {
      break;
    }
  }
  ASSERT_TRUE(!done);
}

CASE(TestAwaitCancelCo6) {
  service::CoroRunnerImpl cm(nullptr);
  bool done = false;
  cm.start_co([&] { done = cm.await_cancel_co(0, []() {}); });
  auto start = util::get_os_time_millionsecond();
  while (true) {
    coro_sched();
    if (util::get_os_time_millionsecond() - start > 500) {
      break;
    }
  }
  ASSERT_TRUE(done);
}

CASE(TestMainCoroCall1) {
  service::CoroRunnerImpl cm(nullptr);
  cm.yield_co();
  cm.try_co();
  cm.wait_co();
  cm.wait_co(100000);
  cm.await_cancel_co(100000, []() {});
}

FIXTURE_END(test_coro_runner)
